package com.basic.business.common.shiro;

import com.alibaba.fastjson.JSON;

import com.basic.business.common.constant.Constants;
import com.basic.business.common.constant.ResultMsgEnum;
import com.basic.business.common.execption.AuthException;
import com.basic.business.utils.Result;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
import org.apache.shiro.web.util.WebUtils;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;


/**
 *  JWT 过滤器。该过滤器只会处理 Shiro 拦截非登录请求
 */
@Slf4j
public class JwtFilter extends BasicHttpAuthenticationFilter {
    private static final String REQUEST_ATTRIBUTE_STATUS = "REQUEST_ATTRIBUTE_STATUS";

    /**
     * 对跨域提供支持
     */
    @Override
    protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
        httpServletResponse.setHeader("Access-control-Allow-Origin", httpServletRequest.getHeader("Origin"));
        httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE");
        httpServletResponse.setHeader("Access-Control-Allow-Headers", httpServletRequest.getHeader("Access-Control-Request-Headers"));
        if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
            httpServletResponse.setStatus(HttpStatus.OK.value());
            return false;
        }
        return super.preHandle(request, response);
    }

    /**
     * 返回true 允许访问
     * 返回false 调用 onAccessDenied 方法
     *
     * @param request
     * @param response
     * @param mappedValue
     * @return
     */
    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        // 登录请求不通过
        if (StringUtils.equals(((HttpServletRequest) request).getRequestURI(), getLoginUrl())) {
            return false;
        }
        // 没带token不通过
        if (((HttpServletRequest) request).getHeader(Constants.REQUEST_TOKEN) == null) {
            return false;
        }
        // 执行登录权限验证
        return executeLogin(request, response);
    }

    @Override
    protected boolean executeLogin(ServletRequest request, ServletResponse response) {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        String token = httpServletRequest.getHeader(Constants.REQUEST_TOKEN);
        CustomerToken jwtToken = new CustomerToken(token);
        try {
            // 创建 JwtToken， 交由 JwtRealm 处理
            Subject subject = getSubject(request, response);
            subject.login(jwtToken);
            return onLoginSuccess(jwtToken, subject, request, response);
        } catch (AuthException e) {
            return onLoginFailure(jwtToken, e, request, response);
        } catch (Exception e) {
            return onLoginFailure(jwtToken, new AuthenticationException(e.getMessage()), request, response);
        }
    }

    @Override
    protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {
        try {
            int code = ResultMsgEnum.LOGIN_EXPIRE.getCode();
            if (e instanceof AuthException) {
                code = ((AuthException) e).getCode();
            }
            HttpServletResponse httpResponse = WebUtils.toHttp(response);
            httpResponse.setCharacterEncoding("UTF-8");
            httpResponse.setContentType("application/json;charset=UTF-8");
            httpResponse.setStatus(HttpStatus.UNAUTHORIZED.value());
            request.setAttribute(REQUEST_ATTRIBUTE_STATUS, true);
            PrintWriter writer = httpResponse.getWriter();
            writer.write(JSON.toJSONString(Result.error(code, e.getMessage(), e.getMessage())));
            return super.onLoginFailure(token, e, request, response);
        } catch (IOException ioException) {
            log.error("[onLoginFailure][ioException = {}]", ioException.getMessage());
        }
        return false;
    }

    /**
     * 请求被拒绝时返回 - 必须重写不能调用父类，否则前端弹窗
     *
     * @param servletRequest
     * @param servletResponse
     * @return
     * @throws Exception
     */
    @Override
    protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
        if (servletRequest.getAttribute(REQUEST_ATTRIBUTE_STATUS) == null) {
            HttpServletResponse httpResponse = WebUtils.toHttp(servletResponse);
            httpResponse.setCharacterEncoding("UTF-8");
            httpResponse.setContentType("application/json;charset=UTF-8");
            httpResponse.setStatus(HttpStatus.UNAUTHORIZED.value());

            PrintWriter writer = httpResponse.getWriter();
            writer.write(JSON.toJSONString(Result.error(ResultMsgEnum.LOGIN_EXPIRE)));
        }


        return false;
    }

}